/**
 * @Package:com.dianwoba.core.transaction
 * 
 * @date 2015-7-15下午1:18:34
 *
 * Copyright @2015 上海点我吧信息技术有限公司 Incorporated. All rights reserved.
 */
package com.dianwoba.core.transaction;

import java.util.Stack;

import org.apache.commons.lang.ArrayUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import com.dianwoba.core.spring.ContextHolder;

/**
 * @author Zhu
 * @date 2015-7-15
 * @version 0.0.1
 * @description
 */
public class MultiTransactionalAspect {

	private Logger logger = LoggerFactory.getLogger(getClass());

	public Object around(ProceedingJoinPoint pjp,
			MultiTransactional multiTransactional) throws Throwable {

		Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack = new Stack<DataSourceTransactionManager>();
		Stack<TransactionStatus> transactionStatuStack = new Stack<TransactionStatus>();

		try {

			if (!openTransaction(dataSourceTransactionManagerStack,
					transactionStatuStack, multiTransactional)) {
				return null;
			}

			Object ret = pjp.proceed();

			commit(dataSourceTransactionManagerStack, transactionStatuStack);

			return ret;
		} catch (Throwable e) {

			rollback(dataSourceTransactionManagerStack, transactionStatuStack);

			logger.warn(String.format(
					"MultiTransactionalAspect, method:%s-%s occors error:", pjp
							.getTarget().getClass().getSimpleName(), pjp
							.getSignature().getName()), e);
			throw e;
		}
	}

	/**
	 * @author Zhu
	 * @date 2015-7-25下午7:55:46
	 * @description
	 * @param dataSourceTransactionManagerStack
	 * @param transactionStatuStack
	 * @param values
	 */
	private boolean openTransaction(
			Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
			Stack<TransactionStatus> transactionStatuStack,
			MultiTransactional multiTransactional) {

		String[] transactionMangerNames = multiTransactional.values();
		if (ArrayUtils.isEmpty(multiTransactional.values())) {
			return false;
		}

		for (String beanName : transactionMangerNames) {
			DataSourceTransactionManager dataSourceTransactionManager = (DataSourceTransactionManager) ContextHolder
					.getBean(beanName);
			TransactionStatus transactionStatus = dataSourceTransactionManager
					.getTransaction(new DefaultTransactionDefinition());
			transactionStatuStack.push(transactionStatus);
			dataSourceTransactionManagerStack
					.push(dataSourceTransactionManager);
		}
		return true;
	}

	/**
	 * @author Zhu
	 * @date 2015-7-25下午7:56:39
	 * @description
	 * @param dataSourceTransactionManagerStack
	 * @param transactionStatuStack
	 */
	private void commit(
			Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
			Stack<TransactionStatus> transactionStatuStack) {
		while (!dataSourceTransactionManagerStack.isEmpty()) {
			dataSourceTransactionManagerStack.pop().commit(
					transactionStatuStack.pop());
		}
	}

	/**
	 * @author Zhu
	 * @date 2015-7-25下午7:56:42
	 * @description
	 * @param dataSourceTransactionManagerStack
	 * @param transactionStatuStack
	 */
	private void rollback(
			Stack<DataSourceTransactionManager> dataSourceTransactionManagerStack,
			Stack<TransactionStatus> transactionStatuStack) {
		while (!dataSourceTransactionManagerStack.isEmpty()) {
			dataSourceTransactionManagerStack.pop().rollback(
					transactionStatuStack.pop());
		}
	}
}
